rpi3: Implement PSCI_SYSTEM_OFF
authorAntonio Nino Diaz <[email protected]>
Sat, 14 Jul 2018 01:15:51 +0000 (02:15 +0100)
committerAntonio Nino Diaz <[email protected]>
Sat, 14 Jul 2018 01:15:51 +0000 (02:15 +0100)
This implementation doesn't actually turn the system off, it simply
reboots it and prevents it from booting while keeping it in a low power
mode.

Change-Id: I7f72c9f43f25ba0341db052bc2be4774c88a7ea3
Signed-off-by: Antonio Nino Diaz <[email protected]>
plat/rpi3/rpi3_hw.h
plat/rpi3/rpi3_pm.c

index 70272e003c3f56b7d8b8e790381a5598c3449758..f411acbca76d6f394145df369137663cf18ebb32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 #define RPI3_IO_BASE                   ULL(0x3F000000)
 #define RPI3_IO_SIZE                   ULL(0x01000000)
 
-/*
- * Serial port (called 'Mini UART' in the BCM docucmentation).
- */
-#define RPI3_IO_MINI_UART_OFFSET       ULL(0x00215040)
-#define RPI3_MINI_UART_BASE            (RPI3_IO_BASE + RPI3_IO_MINI_UART_OFFSET)
-#define RPI3_MINI_UART_CLK_IN_HZ       ULL(500000000)
-
 /*
  * Power management, reset controller, watchdog.
  */
 #define RPI3_PM_BASE                   (RPI3_IO_BASE + RPI3_IO_PM_OFFSET)
 /* Registers on top of RPI3_PM_BASE. */
 #define RPI3_PM_RSTC_OFFSET            ULL(0x0000001C)
+#define RPI3_PM_RSTS_OFFSET            ULL(0x00000020)
 #define RPI3_PM_WDOG_OFFSET            ULL(0x00000024)
 /* Watchdog constants */
-#define RPI3_PM_PASSWORD               ULL(0x5A000000)
-#define RPI3_PM_RSTC_WRCFG_MASK                ULL(0x00000030)
-#define RPI3_PM_RSTC_WRCFG_FULL_RESET  ULL(0x00000020)
+#define RPI3_PM_PASSWORD               U(0x5A000000)
+#define RPI3_PM_RSTC_WRCFG_MASK                U(0x00000030)
+#define RPI3_PM_RSTC_WRCFG_FULL_RESET  U(0x00000020)
+/*
+ * The RSTS register is used by the VideoCore firmware when booting the
+ * Raspberry Pi to know which partition to boot from. The partition value is
+ * formed by bits 0, 2, 4, 6, 8 and 10. Partition 63 is used by said firmware
+ * to indicate halt.
+ */
+#define RPI3_PM_RSTS_WRCFG_HALT                U(0x00000555)
+
+/*
+ * Serial port (called 'Mini UART' in the BCM docucmentation).
+ */
+#define RPI3_IO_MINI_UART_OFFSET       ULL(0x00215040)
+#define RPI3_MINI_UART_BASE            (RPI3_IO_BASE + RPI3_IO_MINI_UART_OFFSET)
+#define RPI3_MINI_UART_CLK_IN_HZ       ULL(500000000)
 
 /*
  * Local interrupt controller
index 1d067fb13df6425ac20f33dd1fb8f739c1958d5f..96948580ec5dc09a3ef2a448b7b55d0da7862dcf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -150,41 +150,61 @@ void rpi3_pwr_domain_on_finish(const psci_power_state_t *target_state)
 }
 
 /*******************************************************************************
- * Platform handler to reboot the system
+ * Platform handlers for system reset and system off.
  ******************************************************************************/
-#define RESET_TIMEOUT  10
 
-static void __dead2 rpi3_system_reset(void)
-{
-       /* Setup watchdog for reset */
+/* 10 ticks (Watchdog timer = Timer clock / 16) */
+#define RESET_TIMEOUT  U(10)
 
-       static const uintptr_t base = RPI3_PM_BASE;
+static void __dead2 rpi3_watchdog_reset(void)
+{
        uint32_t rstc;
 
-       INFO("rpi3: PSCI System Reset: invoking watchdog reset\n");
-
        console_flush();
 
-       rstc = mmio_read_32(base + RPI3_PM_RSTC_OFFSET);
-       rstc &= ~RPI3_PM_RSTC_WRCFG_MASK;
-       rstc |= RPI3_PM_RSTC_WRCFG_FULL_RESET;
-
-       dmbst();
+       dsbsy();
+       isb();
 
-       /*
-        * Watchdog timer = Timer clock / 16
-        * Password (31:16) | Value (11:0)
-        */
-       mmio_write_32(base + RPI3_PM_WDOG_OFFSET,
+       mmio_write_32(RPI3_PM_BASE + RPI3_PM_WDOG_OFFSET,
                      RPI3_PM_PASSWORD | RESET_TIMEOUT);
-       mmio_write_32(base + RPI3_PM_RSTC_OFFSET,
-                     RPI3_PM_PASSWORD | rstc);
+
+       rstc = mmio_read_32(RPI3_PM_BASE + RPI3_PM_RSTC_OFFSET);
+       rstc &= ~RPI3_PM_RSTC_WRCFG_MASK;
+       rstc |= RPI3_PM_PASSWORD | RPI3_PM_RSTC_WRCFG_FULL_RESET;
+       mmio_write_32(RPI3_PM_BASE + RPI3_PM_RSTC_OFFSET, rstc);
 
        for (;;) {
                wfi();
        }
 }
 
+static void __dead2 rpi3_system_reset(void)
+{
+       INFO("rpi3: PSCI_SYSTEM_RESET: Invoking watchdog reset\n");
+
+       rpi3_watchdog_reset();
+}
+
+static void __dead2 rpi3_system_off(void)
+{
+       uint32_t rsts;
+
+       INFO("rpi3: PSCI_SYSTEM_OFF: Invoking watchdog reset\n");
+
+       /*
+        * This function doesn't actually make the Raspberry Pi turn itself off,
+        * the hardware doesn't allow it. It simply reboots it and the RSTS
+        * value tells the bootcode.bin firmware not to continue the regular
+        * bootflow and to stay in a low power mode.
+        */
+
+       rsts = mmio_read_32(RPI3_PM_BASE + RPI3_PM_RSTS_OFFSET);
+       rsts |= RPI3_PM_PASSWORD | RPI3_PM_RSTS_WRCFG_HALT;
+       mmio_write_32(RPI3_PM_BASE + RPI3_PM_RSTS_OFFSET, rsts);
+
+       rpi3_watchdog_reset();
+}
+
 /*******************************************************************************
  * Platform handlers and setup function.
  ******************************************************************************/
@@ -192,6 +212,7 @@ static const plat_psci_ops_t plat_rpi3_psci_pm_ops = {
        .cpu_standby = rpi3_cpu_standby,
        .pwr_domain_on = rpi3_pwr_domain_on,
        .pwr_domain_on_finish = rpi3_pwr_domain_on_finish,
+       .system_off = rpi3_system_off,
        .system_reset = rpi3_system_reset,
        .validate_power_state = rpi3_validate_power_state,
 };